<?php

namespace App\Models;

use App\Common\Exception\ErrorException;
use App\Models\System\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Http\File as  IlluminateFile;

/**
 * 上传文件信息。使用多态关系（Polymorphic Relationships）。
 *
 * @property string $download_url 文件下载地址
 * @property Model $ilk 文件关联的 Model 对象
 */
class Files extends Model
{

    protected $table = 'files';
    /** 园所图片 */
    const ILK_GET_AWARDS = 1;   //获奖情况
    const ILK_PART = 2;   //参与人员

    static private $acceptedType = [
        'pdf' => 'pdf/',
        'mp3' => 'audio/',
        'mp4' => 'video/',
        'doc' => 'word/',
        'docx' => 'word/',
        'xlsx' => 'excel/',
        'xls' => 'excel/',
        'ppt' => 'ppt/',
        'pptx' => 'pptx/',
        'zip' => 'zip/',
        'rar' => 'zip/',
        '7z' => 'zip/',
        'gz' => 'zip/',
        'other'=> 'image/'
    ];
    /**
     * 文件关联对象类型与 model 的映射数组。
     *
     * 当 model 未使用默认的 file_id 字段，或有多个字段关联 File 时，
     * 使用数组分别列出：字段名 => ilk_type
     *
     * @var array
     */
    protected static $model_ilks = [

    ];

    protected $attributes = [
        'download_count' => 0,
    ];

    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::deleted(function ($file) { //删除 model 的同时，删掉物理文件
            Storage::disk($file->getDisk())->delete($file->path);
        });
    }

    public function activityInfo()
    {
        return $this->hasOne(ActivityInfo::class, 'id', 'ilk_id');
    }

    //创建人
    public function creator()
    {
        return $this->hasOne(User::class, 'id', 'creator_user_id');
    }


    /**
     * 多态关系，获取文件对象。
     *
     * @return MorphTo
     */
    public function ilk(): MorphTo
    {
        return $this->morphTo();
    }

    /**
     * 获取 model 与文件关联对象类型（ilk_type）的映射数组
     *
     * @return array
     */
    public static function getModelIlkTypes()
    {
        return static::$model_ilks;
    }

    /**
     * 获取某一 model 与文件关联对象类型（ilk_type）
     *
     * @param string $model_class Model类名
     *
     * @return null|integer|array
     */
    public static function getModelIlkType($model_class)
    {
        return static::$model_ilks[$model_class] ?? null;
    }

    /**
     * 保存上传文件至文件表中。
     *
     * @param \Illuminate\Http\UploadedFile $upfile   上传文件
     * @param integer                       $ilk_type 文件类别
     *
     * @return array
     */
    public static function storeFile(\Illuminate\Http\UploadedFile $files, $ilk_type = null, $creator_user_id = null, $group = null)
    {

        //获取文件类型
        $extension = $files->getClientOriginalExtension();
        $folder = self::$acceptedType[strtolower($files->getClientOriginalExtension())] ?? self::$acceptedType['other'];
        $path = Carbon::now()->format('Ymd');
        $tempName = md5($files->getClientOriginalName().uniqid().$path).'.'.$extension;
        $allowType = ['doc','docx','xls','xlsx','ppt','pptx','pdf','jpg','jpeg','gif','png','rar','zip','mp4'];
        if(!in_array($extension, $allowType)){
            throw new ErrorException('无法上传该类型文件');
            //throw new \Exception('无法上传该类型文件');
        }
        $newPath = $folder.date('Ym');
        $file = Storage::disk('backend')->putFileAs($newPath, new IlluminateFile($files),$tempName);
        $hash = Str::uuid() . '';
//        Storage::disk('public')->makeDirectory($path);
        $size = $files->getSize();

//        $upfile->storeAs($path, $name, 'public');
        $fileModel = new static([
            'hash'      => $hash,
            'name'      => $files->getClientOriginalName(),
            'path'      => Storage::disk('backend')->url($file),
            'size'      => $size,
            'mime_type' => extension2mimeType(strtolower($files->getClientOriginalExtension())),
            'ilk_type'  => $ilk_type,
            'group'     => $group,
            'creator_user_id'     => auth()->id() ?: '',
        ]);
        $fileModel->save();
        return [
            'url' => Storage::disk('backend')->url($file),
            'file_name' => $files->getClientOriginalName(),
            'size' => $files->getSize(),
//            'hash' => $hash,
            'files_id' => $fileModel->id,
            'date' => date("Y-m-d H:i:s"),
            'type' => extension2mimeType($files->getClientOriginalExtension())
        ];
    }

    /**
     * 获取上传的文件数据
     *
     * @param int|array $file_ids
     *
     * @return array
     */
    public static function getFiles($file_ids)
    {
        if (!is_array($file_ids)) {
            $file_ids = [$file_ids];
        }
        $files = File::whereIn('id', $file_ids)
                    ->get(['id', 'hash', 'name', 'path']);
        $arr = [];
        foreach ($files as $file) { /** @var static $file */
            $arr[] = array_merge($file->toArray(), ['url'=>$file->download_url]);
        }
        return $arr;
    }

    /**
     * 复制为新的文件
     *
     * @return \App\Models\File
     */
    public function copyAsNewFile()
    {
        $file = $this->replicate(['download_count', 'ilk_type', 'ilk_id', 'creator_user_id']);
        $hash = Str::uuid() . '';
        $path = '/uploads/' . substr($hash, 0, 1) . '/' . substr($hash, 1, 1);
        $ext  = pathinfo($file->name, \PATHINFO_EXTENSION);
        $name = $hash . '.' . $ext;
        $disk = $this->getDisk();
        Storage::disk($disk)->makeDirectory($path);
        Storage::disk($disk)->copy($file->path, $path . '/' . $name);
        $file->path = $path . '/' . $name;
        $file->hash = $hash;
        $file->save();
        return $file;
    }

    /**
     * 获取文件存储的 disk
     *
     * @return string
     */
    public function getDisk()
    {
        return 'public';
    }

    /**
     * 获取文件下载地址
     *
     * @return string
     */
    public function getDownloadUrlAttribute()
    {
        return route('files.download', ['hash' => $this->hash]);
    }

    /**
     * 获取文件完整物理路径
     *
     * @return string
     */
    public function getFullPath()
    {
        return Storage::disk($this->getDisk())->path($this->path);
    }
}
